<script setup>
import {onMounted, onUnmounted, ref, watch, reactive, computed} from "vue";
import * as THREE from "three";
import * as TWEEN from '@tweenjs/tween.js'
import {CSS3DObject, CSS3DRenderer, TrackballControls} from "three/addons";
import {ElMessageBox} from 'element-plus'

import Background from "@components/common/background.vue";
import useLotteryStore from '@stores/lottery.js'

const lottery_store = useLotteryStore();

// DOM 相关
const audio = ref();
const container = ref();
const audio_controller = ref();


// 暂停/播放 音乐
const play_state = ref(false);
let music_stop, deg = 0;
watch(play_state, function (val) {
  playMusic(val);
});

/**
 * 播放音乐
 * @param status
 */
function playMusic(status) {
  clearTimeout(music_stop)
  if (status) {
    audio.value?.play();

    (function route() {
      deg += 1;
      if (deg >= 360) {
        deg = 0;
      }
      audio_controller.value.style.transform = `rotate(${deg}deg)`;
      music_stop = setTimeout(route, 20);
    }());

    return;
  }
  audio?.value?.pause();
}

// 奖品相关
const store_lottery_list = lottery_store.getList(); // 奖品信息
const set_lottery_list = JSON.parse(JSON.stringify(store_lottery_list));

// 抽奖状态相关
// 是否进入抽奖页面
const enter_lottery = ref(false);
// 是否正在抽奖
const is_lottery = ref(false);

// 中奖人员数据
const lucky_users = [];
// 中奖人集合
let current_luckily = [];
// 未中奖人数集合
let left_users = [];


// 初始化一个无限大的数 用于标志当前抽的是哪个奖项
let current_prize_index = store_lottery_list.length - 1;
// 当前奖项
let current_prize = store_lottery_list[current_prize_index];

onMounted(function () {
  // 初始化播放音乐
  if (play_state.value) {
    playMusic(true);
  }
  initCards();
  addHighlight();
  updateScene();
  shineCard();
  transform(targets.table, 2000);
  animate();
  window.addEventListener('resize', onWindowResize);
});

onUnmounted(function () {
  playMusic(false);
  cancelAnimationFrame(stop);
  clearTimeout(shine_stop);
  window.removeEventListener('resize', onWindowResize);
});

/**
 * 处理按钮点击事件
 */
function menu(event) {
  const target = event.target;
  console.log(target.id);
  if (getLotteryStatus()) {
    if (target.id === 'lottery') {
      rotateObj.stop();
      setLotteryStatus(false);
      return;
    }
    ElMessageBox.alert('正在抽奖中……');
    return;
  }
  switch (target.id) {
    case 'enter':
      transform(targets.sphere, 2000);
      removeHighlight();
      enter_lottery.value = true;
      // 进入抽奖
      break;
    case 'reset':
      // 重置抽奖
      transform(targets.table, 2000);
      addHighlight();
      enter_lottery.value = false;
      // todo 重置一些配置
      break;
    case 'lottery':
      // 开始抽奖
      setLotteryStatus(true);
      // 每次抽奖前先保存上一次的抽奖数据
      // saveData();
      //更新剩余抽奖数目的数据显示
      // changePrize();
      resetCard().then(res => {
        // 抽奖
        lottery();
      });
      break;
  }
}

function setLotteryStatus(status) {
  is_lottery.value = status;
}

function getLotteryStatus() {
  return is_lottery.value;
}


const NUMBER_MATRIX = [
  [
    // 0
    [0, 0],
    [1, 0],
    [2, 0],
    [0, 1],
    [2, 1],
    [0, 2],
    [2, 2],
    [0, 3],
    [2, 3],
    [0, 4],
    [1, 4],
    [2, 4]
  ],
  [
    // 1
    [1, 0],
    [0, 1],
    [1, 1],
    [1, 2],
    [1, 3],
    [0, 4],
    [1, 4],
    [2, 4]
  ],
  [
    // 2
    [0, 0],
    [1, 0],
    [2, 0],
    [2, 1],
    [0, 2],
    [1, 2],
    [2, 2],
    [0, 3],
    [0, 4],
    [1, 4],
    [2, 4]
  ],
  [
    // 3
    [0, 0],
    [1, 0],
    [2, 0],
    [2, 1],
    [0, 2],
    [1, 2],
    [2, 2],
    [2, 3],
    [0, 4],
    [1, 4],
    [2, 4]
  ],
  [
    // 4
    [0, 0],
    [2, 0],
    [0, 1],
    [2, 1],
    [0, 2],
    [1, 2],
    [2, 2],
    [2, 3],
    [2, 4]
  ],
  [
    // 5
    [0, 0],
    [1, 0],
    [2, 0],
    [0, 1],
    [0, 2],
    [1, 2],
    [2, 2],
    [2, 3],
    [0, 4],
    [1, 4],
    [2, 4]
  ],
  [
    // 6
    [0, 0],
    [1, 0],
    [2, 0],
    [0, 1],
    [0, 2],
    [1, 2],
    [2, 2],
    [0, 3],
    [2, 3],
    [0, 4],
    [1, 4],
    [2, 4]
  ],
  [
    // 7
    [0, 0],
    [1, 0],
    [2, 0],
    [2, 1],
    [2, 2],
    [2, 3],
    [2, 4]
  ],
  [
    // 8
    [0, 0],
    [1, 0],
    [2, 0],
    [0, 1],
    [2, 1],
    [0, 2],
    [1, 2],
    [2, 2],
    [0, 3],
    [2, 3],
    [0, 4],
    [1, 4],
    [2, 4]
  ],
  [
    // 9
    [0, 0],
    [1, 0],
    [2, 0],
    [0, 1],
    [2, 1],
    [0, 2],
    [1, 2],
    [2, 2],
    [2, 3],
    [0, 4],
    [1, 4],
    [2, 4]
  ]
];

const ROTATE_TIME = 3000;
const ROTATE_LOOP = 1000;
const BASE_HEIGHT = 1080;

let rotateObj = undefined;


let ROW_COUNT      = 7,
    COLUMN_COUNT   = 17,
    TOTAL_CARDS    = ROW_COUNT * COLUMN_COUNT,
    btns           = {
      enter:      document.querySelector("#enter"),
      lotteryBar: document.querySelector("#lotteryBar"),
      lottery:    document.querySelector("#lottery")
    },
    prizes,
    EACH_COUNT     = [1],
    COMPANY, // 公司标识
    HIGHLIGHT_CELL = [],
    // 当前的比例
    Resolution     = 1;

COMPANY = '温馨小家';
HIGHLIGHT_CELL = createHighlight(2156);


let camera,
    scene,
    renderer,
    controls,
    threeDCards         = [],
    selected_card_index = [], // 已选择的卡
    targets             = {
      table:  [],
      sphere: []
    };

let stop;

/**
 * 创建元素
 */
function createElement(css, text) {
  let dom = document.createElement("div");
  dom.className = css || "";
  dom.innerHTML = text || "";
  return dom;
}

/**
 * 创建名牌
 */
function createCard(user, is_bold, id, showTable) {
  const element = createElement();
  element.id = "card-" + id;
  element.className = 'element';
  element.style.backgroundColor = "rgba(0,127,127," + (random(70, 25) / 100) + ")";

  if (is_bold) {
    element.classList.add('light-item');
    if (showTable) {
      element.classList.add('highlight');
    }
  }
  //添加公司标识
  element.appendChild(createElement("company", COMPANY));

  element.appendChild(createElement("name", user.name));

  element.appendChild(createElement("details", user.id + "<br/>" + user.department));
  return element;
}

function removeHighlight() {
  document.querySelectorAll(".highlight").forEach(node => {
    node.classList.remove("highlight");
  });
}

function addHighlight() {
  document.querySelectorAll(".light-item").forEach(node => {
    node.classList.add("highlight");
  });
}

/**
 * 创建 高亮 组合成当年年份数字
 * @return {*[]}
 */
function createHighlight(year = undefined) {
  year = Number(year);
  if (!year || isNaN(year) || year < 0) {
    year = new Date().getFullYear();
  }
  year = year + '';
  let step      = 4,
      x_offset  = 1,
      y_offset  = 1,
      highlight = [];

  year.split("").forEach(n => {
    highlight = highlight.concat(
      NUMBER_MATRIX[n].map(item => {
        return `${item[0] + x_offset}-${item[1] + y_offset}`;
      })
    );
    x_offset += step;
  });

  return highlight;
}

/**
 * 随机整数
 * @param max 最大值
 * @param min 最小值
 * @return {number}
 */
function random(max, min = 0) {
  let val = max - min;
  if (!val) {
    return max;
  }
  if (val < 0) {
    val = min - max;
    min = max;
  }
  return Math.floor(Math.random() * 10000000 & val) + min;
}


/**
 * 切换名牌人员信息
 * @param {number} index
 * @param {{name,id,department}} user
 */
function changeCard(index, user) {
  let card = threeDCards[index].element;
  card.innerHTML = `<div class="company">${COMPANY}</div><div class="name">${user.name}</div><div class="details">${user.id || ""}<br/>${user.department || "PSST"}</div>`;
}

/**
 * 切换名牌背景
 */
function shine(index, color) {
  let card = threeDCards[index].element;
  card.style.backgroundColor = color || "rgba(0,127,127," + (random(70, 25) / 100) + ")";
}

// 用于清除切换背景
let shine_stop;

/**
 * 随机切换背景和人员信息
 */
function shineCard() {
  let shine_max = 50, shine_min = 10,
      max_user;
  const users = lottery_store.getUsers();

  (function shan() {
    shine_stop = setTimeout(shan, 300);
    // 正在抽奖停止闪烁
    if (is_lottery.value) {
      return;
    }
    max_user = users.length - 1;
    for (let i = 0; i < random(shine_max, shine_min); i++) {
      let index      = random(max_user),
          card_index = random(TOTAL_CARDS - 1);
      // 当前显示的已抽中名单不进行随机切换
      if (selected_card_index.includes(card_index)) {
        continue;
      }
      shine(card_index);
      changeCard(card_index, users[index]);
    }
  }());
}

/**
 * 切换场景
 */
function transform(targets, duration) {
  // TWEEN.removeAll();
  for (let i = 0; i < threeDCards.length; i++) {
    const object = threeDCards[i];
    const target = targets[i];

    new TWEEN.Tween(object.position)
      .to(
        {
          x: target.position.x,
          y: target.position.y,
          z: target.position.z
        },
        Math.random() * duration + duration
      )
      .easing(TWEEN.Easing.Exponential.InOut)
      .start();

    new TWEEN.Tween(object.rotation)
      .to(
        {
          x: target.rotation.x,
          y: target.rotation.y,
          z: target.rotation.z
        },
        Math.random() * duration + duration
      )
      .easing(TWEEN.Easing.Exponential.InOut)
      .start();
  }

  new TWEEN.Tween(this)
    .to({}, duration * 2)
    .onUpdate(updateScene)
    .start();
}

function initCards() {
  let member    = /*basicData.users.slice()*/ lottery_store.getUsers(),
      showCards = [],
      length    = member.length;

  let isBold      = false,
      showTable   = /*basicData.leftUsers.length === basicData.users.length*/ true,
      index       = 0,
      totalMember = member.length,
      position    = {
        x: (140 * COLUMN_COUNT - 20) / 2,
        y: (180 * ROW_COUNT - 20) / 2
      };

  camera = new THREE.PerspectiveCamera(
    40,
    window.innerWidth / window.innerHeight,
    1,
    10000
  );
  camera.position.z = 3000;

  scene = new THREE.Scene();

  for (let i = 0; i < ROW_COUNT; i++) {
    for (let j = 0; j < COLUMN_COUNT; j++) {
      isBold = HIGHLIGHT_CELL.includes(j + "-" + i);
      const element = createCard(
        member[index % length],
        isBold,
        index,
        showTable
      );

      let object = new CSS3DObject(element);
      object.position.x = Math.random() * 4000 - 2000;
      object.position.y = Math.random() * 4000 - 2000;
      object.position.z = Math.random() * 4000 - 2000;
      scene.add(object);
      threeDCards.push(object);
      //

      object = new THREE.Object3D();
      object.position.x = j * 140 - position.x;
      object.position.y = -(i * 180) + position.y;
      targets.table.push(object);
      index++;
    }
  }

  // sphere

  const vector = new THREE.Vector3();

  for (let i = 0, l = threeDCards.length; i < l; i++) {
    const phi = Math.acos(-1 + (2 * i) / l);
    const theta = Math.sqrt(l * Math.PI) * phi;
    const object = new THREE.Object3D();
    object.position.setFromSphericalCoords(800 * Resolution, phi, theta);
    vector.copy(object.position).multiplyScalar(2);
    object.lookAt(vector);
    targets.sphere.push(object);
  }

  renderer = new CSS3DRenderer();
  renderer.setSize(window.innerWidth, window.innerHeight);
  container.value.appendChild(renderer.domElement);

  //

  controls = new TrackballControls(camera, renderer.domElement);
  controls.rotateSpeed = 3.5;
  controls.minDistance = 500;
  controls.maxDistance = 6000;
  controls.addEventListener("change", updateScene);

  // bindEvent();

  // if (showTable) {
  //   switchScreen("enter");
  // } else {
  //   switchScreen("lottery");
  // }
  // renderer.render(scene, camera);
}


function checkLottery(count) {
  return new Promise((s, f) => {
    // 检查人员数量 奖品数量 抽取数量
    // 一次性奖品抽取数量 count
    // const pre_prize_count = current_prize.count;
    // 可抽取总人数
    const total = current_prize.total;

    // 当前中奖人数 已经抽的中奖人数
    const lucky_data = lucky_users[current_prize.id];
    // 剩余抽奖人数
    const left_prize_count = total - (lucky_data ? lucky_data.length : 0);

    if (left_prize_count < left_users.length) {
      f(new Error(`剩余人数不足以抽奖。`));
      return;
    }

    if (left_prize_count < count) {
      ElMessageBox.confirm(
        `抽奖人数剩余${left_prize_count}个，是否将抽奖个数改为${left_prize_count}个`,
        'Warning',
        {
          confirmButtonText: '是',
          cancelButtonText:  '否',
          type:              'warning',
        }
      ).then(function () {
        s(left_prize_count);
      }).catch(function (e) {
        f(e);
      })
      return;
    }
    s(count);
  });
}

/**
 * 开始抽奖
 * @param count
 */
function lottery(count) {
  console.log('开始抽奖', count);

  checkLottery(count).then(function (count) {
    is_lottery.value = true;
    rotateBall().then(() => {
      // 将之前的记录置空
      current_luckily = [];
      selected_card_index = [];
      // 当前同时抽取的数目,当前奖品抽完还可以继续抽，但是不记录数据
      let perCount       = EACH_COUNT[current_prize_index],
          luckyData      = lucky_users[current_prize.type],
          leftCount      = lottery_store.users.length,
          leftPrizeCount = current_prize.count - (luckyData ? luckyData.length : 0);

      if (leftCount < perCount) {
        // addQipao("剩余参与抽奖人员不足，现在重新设置所有人员可以进行二次抽奖！");
        // basicData.leftUsers = basicData.users.slice();
        // leftCount = basicData.leftUsers.length;
      }

      for (let i = 0; i < perCount; i++) {
        let luckyId = random(leftCount);
        current_luckily.push(basicData.leftUsers.splice(luckyId, 1)[0]);
        leftCount--;
        leftPrizeCount--;

        let cardIndex = random(TOTAL_CARDS);
        while (selected_card_index.includes(cardIndex)) {
          cardIndex = random(TOTAL_CARDS);
        }
        selected_card_index.push(cardIndex);

        if (leftPrizeCount === 0) {
          break;
        }
      }

      // console.log(lucky.current);
      selectCard();
    });
  }).catch(function (e) {
    console.log(e);
  });
}

/**
 * 旋转球体
 * @returns {Promise<unknown>}
 */
function rotateBall() {
  return new Promise((resolve, reject) => {
    scene.rotation.y = 0;
    rotateObj = (rotateObj || new TWEEN.Tween(scene.rotation));
    rotateObj
      .to(
        {
          y: Math.PI * 6 * ROTATE_LOOP
        },
        ROTATE_TIME * ROTATE_LOOP
      )
      .onUpdate(updateScene)
      // .easing(TWEEN.Easing.Linear)
      .start()
      .onStop(createCallback(resolve))
      .onComplete(createCallback(resolve));
  });

  function createCallback(resolve) {
    return function () {
      scene.rotation.y = 0;
      updateScene();
      resolve();
    }
  }
}

/**
 * 重置抽奖牌内容
 */
function resetCard(duration = 500) {
  if (current_luckily.length === 0) {
    return Promise.resolve();
  }

  selected_card_index.forEach(index => {
    let object = threeDCards[index],
        target = targets.sphere[index];

    new TWEEN.Tween(object.position)
      .to(
        {
          x: target.position.x,
          y: target.position.y,
          z: target.position.z
        },
        Math.random() * duration + duration
      )
      .easing(TWEEN.Easing.Exponential.InOut)
      .start();

    new TWEEN.Tween(object.rotation)
      .to(
        {
          x: target.rotation.x,
          y: target.rotation.y,
          z: target.rotation.z
        },
        Math.random() * duration + duration
      )
      .easing(TWEEN.Easing.Exponential.InOut)
      .start();
  });

  return new Promise((resolve, reject) => {
    new TWEEN.Tween(this)
      .to({}, duration * 2)
      .onUpdate(updateScene)
      .start()
      .onComplete(() => {
        selected_card_index.forEach(index => {
          let object = threeDCards[index];
          object.element.classList.remove("prize");
        });
        resolve();
      });
  });
}


function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
  updateScene();
}

/**
 * 场景变换
 */
function updateScene() {
  renderer.render(scene, camera);
}

function animate() {
  controls?.update();
  TWEEN.update();
  stop = requestAnimationFrame(animate);
}

const number_dialog = reactive({
  show:    false,
  select:  1,
  options: [
    {value: 1, label: '抽一个'},
    {value: 3, label: '抽三个'},
    {value: 5, label: '抽五个'},
    {value: 0, label: '自定义'},
  ],
  value:   1,
});

</script>

<template>
  <div class="lottery">
    <div class="music">
      <audio ref="audio" src="/sounds/background.mp3" class="music-item" loop />
      <div ref="audio_controller" class="music-box" @click="play_state = !play_state" :title="$t('lottery.music.title')">{{ $t('lottery.music.text') }}</div>
    </div>
    <div class="prize-bar">
      <div class="title">
        正在抽取 <span class="prize-content">特等奖</span> <span class="prize-content">皮鞋</span> , 剩余 <span class="prize-content">1</span> <span>只</span>
      </div>
      <ul class="list">
        <li class="prize-item" v-for="item in store_lottery_list">
          <span></span><span></span><span></span><span></span>
          <div class="prize-img">
            <img src="" alt="">
          </div>
          <div class="prize-text">
            <h5>{{ item.content }}</h5>
            <div class="prize-count">
              <el-progress
                :stroke-width="15"
                striped
                striped-flow />
            </div>
          </div>
        </li>
      </ul>
    </div>
    <div class="container" ref="container"></div>
    <div class="menu" @click.stop="menu">
      <button id="enter" v-show="!enter_lottery" class="enter animate">进入抽奖</button>
      <div v-show="enter_lottery" class="lottery-bar" ref="lottery-bar">
        <button v-if="getLotteryStatus()" id="lottery" class="lottery animate">结束抽奖</button>
        <button v-else id="lottery" class="lottery animate" @click.stop="number_dialog.show = true">开始抽奖</button>
        <button id="re-lottery" class="re-lottery">重新抽奖</button>
        <div class="fixed-bar">
          <button id="show" class="fixed-btn show">抽奖结果</button>
          <button id="export" class="fixed-btn export">导出抽奖</button>
          <button id="reset" class="fixed-btn reset">重置抽奖</button>
        </div>
      </div>
    </div>
  </div>
  <el-dialog title="抽奖个数" v-model="number_dialog.show" width="50vh">
    <el-row :gutter="10">
      <el-col :span="12">
        <el-select v-model="number_dialog.select">
          <el-option v-for="item in number_dialog.options" :key="item.value" :value="item.value" :label="item.label" />
        </el-select>
      </el-col>
      <el-col :span="12">
        <el-input-number v-show="!number_dialog.select" :controls="false" v-model="number_dialog.value" :min="1" :max="15" />
      </el-col>
    </el-row>
    <template #footer>
      <span class="dialog-footer">
        <el-button @click="number_dialog.show = false">取 消</el-button>
        <el-button type="primary" @click="number_dialog.show = false;lottery(number_dialog.select || number_dialog.value);">
          确 认
        </el-button>
      </span>
    </template>
  </el-dialog>
  <background />
</template>

<style scoped>
.prize-bar {color: white;}
.lottery {

}
.menu button {color: rgba(127, 255, 255, 0.75);background: transparent;outline: 1px solid rgba(127, 255, 255, 0.75);border: 0;padding: 1.6vh 4vh;margin: 0 4.6vh;font-size: 2vh;font-weight: bold;cursor: pointer;}
.menu button:hover {background-color: rgba(0, 255, 255, 0.5);}

.music {position: fixed;top: 3vh;right: 4vh;z-index: 9;}
.music-box {--length: 5vh;cursor: pointer;display: block;width: var(--length);height: var(--length);line-height: var(--length);color: #fff;border-radius: 50%;text-align: center;font-size: 1.4vh;background: rgba(253, 105, 0, 0.9);}

.prize-bar {position: fixed;top: 10px;left: 1.5vh;z-index: 2156}
.prize-bar .title {display: flex;align-items: center}
.prize-bar .title .prize-content {color: red; font-size: 32px;font-weight: bold;margin: 0 15px;}

.container {margin: 0 0 0 15vh;position: relative;z-index: 3;overflow: hidden}


.menu {position: absolute;bottom: 2vh;width: calc(100vw - 15vh);text-align: center;z-index: 2156;margin-left: 15vh;}
.menu .animate {-webkit-animation: breath 1.6s linear infinite;animation: breath 1.6s linear infinite;box-shadow: 0 0 15px rgb(127 255 255 / 75%);}
.fixed-bar {position: fixed;bottom: 20px;right: 20px;}
.fixed-btn {margin: 20px 0 0;width: 150px;text-align: center;display: block;padding: 1vh 2vh;font-size: 15px;}

@keyframes breath {
  0% {transform: scale(0.9);opacity: 0.8;}
  50% {transform: scale(1.1);opacity: 1;}
  100% {transform: scale(0.9);opacity: 0.8;}
}
</style>

<style>
.container .element {width: 12vh;height: 16vh;box-shadow: 0 0 12px rgba(0, 255, 255, 0.5);border: 1px solid rgba(127, 255, 255, 0.25);text-align: center;cursor: default;transition: background-color 0.3s ease-in;}
.container .element:hover {box-shadow: 0 0 12px rgba(0, 255, 255, 0.75);border: 1px solid rgba(127, 255, 255, 0.75);}
.container .element .company {position: absolute;top: 1.2vh;right: 0;width: 100%;font-size: 2vh;color: rgba(127, 255, 255, 0.75);}
.container .element .name {position: absolute;top: 4.6vh;left: 0;right: 0;font-size: 2.9vh;font-weight: bold;color: rgba(255, 255, 255, 0.75);text-shadow: 0 0 1vh rgba(0, 255, 255, 0.95);}
.container .element .details {position: absolute;bottom: 1.2vh;left: 0;right: 0;font-size: 1.6vh;color: rgba(127, 255, 255, 0.75);}
.container .element.highlight {background-color: rgba(253, 105, 0, 0.95) !important;box-shadow: 0 0 12px rgba(253, 105, 0, 0.95);border: 1px solid rgba(253, 105, 0, 0.25);}
</style>
